//===- main.cpp -----------------------------------------------------------===//
//
//                     The MCLinker Project
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include <mcld/PreferenceOptions.h>
#include <mcld/TripleOptions.h>
#include <mcld/DynamicSectionOptions.h>
#include <mcld/OutputFormatOptions.h>
#include <mcld/SearchPathOptions.h>
#include <mcld/OptimizationOptions.h>
#include <mcld/SymbolOptions.h>
#include <mcld/TargetControlOptions.h>
#include <mcld/ScriptOptions.h>
#include <mcld/PositionalOptions.h>

#include <mcld/Module.h>
#include <mcld/Environment.h>
#include <mcld/LinkerConfig.h>
#include <mcld/LinkerScript.h>
#include <mcld/Linker.h>
#include <mcld/IRBuilder.h>
#include <mcld/MC/InputAction.h>
#include <mcld/Support/raw_ostream.h>
#include <mcld/Support/MsgHandling.h>
#include <llvm/Support/ManagedStatic.h>
#include <llvm/Support/Signals.h>
#include <string>
#include <cassert>
#include <cstdlib>

/// configure linker
static inline bool ConfigLinker(
    int pArgc,
    char* pArgv[],
    const char* pName,
    mcld::Module& pModule,
    mcld::LinkerScript& pScript,
    mcld::LinkerConfig& pConfig,
    mcld::IRBuilder& pBuilder,
    std::vector<mcld::InputAction*>& pInputActions) {
  mcld::PreferenceOptions preference;
  mcld::TripleOptions triple;
  mcld::DynamicSectionOptions dynamic_section;
  mcld::OutputFormatOptions output_format;
  mcld::SearchPathOptions search_path;
  mcld::OptimizationOptions optimization;
  mcld::SymbolOptions symbol;
  mcld::TargetControlOptions target_control;
  mcld::ScriptOptions script;
  mcld::PositionalOptions positional;

  llvm::cl::ParseCommandLineOptions(pArgc, pArgv, pName);

  if (!preference.parse(pConfig))
    return false;

  if (!triple.parse(pArgc, pArgv, pConfig))
    return false;

  if (!dynamic_section.parse(pConfig, pScript))
    return false;

  if (!output_format.parse(pModule, pConfig))
    return false;

  if (!search_path.parse(pConfig, pScript))
    return false;

  if (!optimization.parse(pConfig))
    return false;

  if (!symbol.parse(pConfig))
    return false;

  if (!target_control.parse(pConfig))
    return false;

  if (!script.parse(pScript))
    return false;

  if (!positional.parse(pInputActions, pConfig, pScript))
    return false;

  if (pConfig.options().soname().empty())
    pConfig.options().setSOName(pModule.name());

  return true;
}

static inline bool InitializeInputs(
    mcld::IRBuilder& pBuilder,
    std::vector<mcld::InputAction*>& pInputActions) {
  for (
      std::vector<mcld::InputAction*>::iterator action = pInputActions.begin(),
                                                actionEnd = pInputActions.end();
      action != actionEnd;
      ++action) {
    assert(*action != NULL);
    (*action)->activate(pBuilder.getInputBuilder());
    delete *action;
  }

  if (pBuilder.getInputBuilder().isInGroup()) {
    mcld::fatal(mcld::diag::fatal_forbid_nest_group);
    return false;
  }

  return true;
}

int main(int argc, char* argv[]) {
  llvm::sys::PrintStackTraceOnErrorSignal();
  llvm::llvm_shutdown_obj Y;  // Call llvm_shutdown() on exit.
  mcld::Initialize();

  mcld::LinkerScript script;
  mcld::LinkerConfig config;
  mcld::Module module(script);
  mcld::IRBuilder builder(module, config);
  std::vector<mcld::InputAction*> input_actions;

  if (!ConfigLinker(argc,
                    argv,
                    "MCLinker\n",
                    module,
                    script,
                    config,
                    builder,
                    input_actions)) {
    mcld::errs() << argv[0]
                 << ": failed to process linker options from command line!\n";
    return EXIT_FAILURE;
  }

  mcld::Linker linker;
  if (!linker.emulate(script, config)) {
    mcld::errs() << argv[0] << ": failed to emulate target!\n";
    return EXIT_FAILURE;
  }

  // FIXME: is it possible to have a lightweight MCLinker pass?
  if (!InitializeInputs(builder, input_actions)) {
    mcld::errs() << argv[0] << ": failed to initialize input tree!\n";
    return EXIT_FAILURE;
  }

  if (!linker.link(module, builder)) {
    mcld::errs() << argv[0] << ": failed to link objects!\n";
    return EXIT_FAILURE;
  }

  if (!linker.emit(module, module.name())) {
    mcld::errs() << argv[0] << ": failed to emit output!\n";
    return EXIT_FAILURE;
  }

  mcld::Finalize();

  return EXIT_SUCCESS;
}
